#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
#include "Mutex.hpp"
#include "Cond.hpp"

static int gcap = 10;

namespace BlockQueueModule
{
    using namespace MutexModule;
    using namespace CondModule;

    template <typename T>
    class BlockQueue
    {
        bool IsFull() { return _q.size() == _cap; }
        bool IsEmpty() { return _q.empty(); }

    public:
        BlockQueue(int cap = gcap) : _cap(cap), _cwait_num(0), _pwait_num(0)
        {}

        // 生产者生产数据
        void Equeue(const T &in)
        {
            LockGuard lock(_mutex);//函数作用域范围内的临时变量
            
            while (IsFull()) // 对条件进行判断，为了防止发生伪判断，进行循环判断
            {
                std::cout << "生产者进行等待" << std::endl;
                _pwait_num++;

                _productor_cond.Wait(_mutex); 

                _pwait_num--;
                
                std::cout << "生产者醒来了" << std::endl;
            }

            // IsFull不满足或者线程醒过来了
            _q.push(in); // 生产

            if (_cwait_num) // 此时肯定有数据，可以唤醒等待队列里面的消费者
            {
                std::cout << "唤醒消费者" << std::endl;
                _consumer_cond.Notify();
            }

            // 出了函数作用域，销毁：释放锁
        }

        // 消费者消费数据
        void Pop(T *out)
        {
            LockGuard lock(_mutex);

            while (IsEmpty())
            {
                std::cout << "消费者进行等待" << std::endl;

                _cwait_num++;
                _consumer_cond.Wait(_mutex); // wait的时候是一定是持有锁的，wait的时候，会释放锁
                _cwait_num--;

                std::cout << "消费者醒来了" << std::endl;
            }

            *out = _q.front();
            _q.pop();

            if (_pwait_num) // 生产者消费数据了，可以让生产者生产了
            {
                std::cout << "唤醒生产者" << std::endl;
               _productor_cond.Notify();
            }
        }

        ~BlockQueue()
        {}

    private:
        std::queue<T> _q;     // 保存数据的容器：临界资源
        int _cap;             // 最大容量
        Mutex _mutex;         // 互斥量
        Cond _productor_cond; // 消费者条件变量
        Cond _consumer_cond;  // 生产者条件变量

        int _cwait_num;
        int _pwait_num;
    };
}